package com.spsoft.vatoutinv.service.generateinvoice.impl;

import com.spsoft.vatoutinv.constants.OfdConstant;
import com.spsoft.vatoutinv.enums.YhzcbsEnum;
import com.spsoft.vatoutinv.service.generateinvoice.GenerateInvoiceService;
import com.spsoft.vatoutinv.util.ofd.entity.OriginalInvoice;
import com.spsoft.vatoutinv.util.ofd.entity.realestate.RealEstateSalesInfo;
import liquibase.util.StringUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.ofdrw.core.basicStructure.pageObj.layer.Type;
import org.ofdrw.layout.OFDDoc;
import org.ofdrw.layout.PageLayout;
import org.ofdrw.layout.VirtualPage;
import org.ofdrw.layout.element.Paragraph;
import org.ofdrw.layout.element.Position;
import org.ofdrw.layout.element.Span;
import org.ofdrw.layout.element.canvas.Canvas;
import org.ofdrw.layout.element.canvas.DrawContext;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;

/**
 * @author wangy
 * @date 2024/2/20 9:46
 */
@Slf4j
@Service
public class RealEstateSalesInvoice extends CommonInvoice implements GenerateInvoiceService {

    @Override
    public String generateInvoice(OriginalInvoice originalInvoice, String outPath) throws IOException {
        final Path path = Paths.get(outPath).toAbsolutePath();

        try (OFDDoc ofdDoc = new OFDDoc(path)) {
            // 计算明细所需行数 不同发票类型明细信息不同
            int maxRow = calculateNumberRows(originalInvoice.getRealEstateSalesInfo());

            // 根据行数判断是否需要分页 当 42<n行时就需要换页 少于42 默认一页
            int page = maxRow >= 42 ? maxRow / 42 + 1 : 1;
            // 设置一个记录行数的变量 用于后续比生成明细时 通过删减变量判断是否需要翻页
            int tempRow = 0;
            // 设置一个记录明细下标的变量
            int index = 0;
            // 定义当前页面 小计
            // 小计金额
            BigDecimal subTotalAmount = new BigDecimal(0);
            // 小计税额
            BigDecimal subTotalTaxAmount = new BigDecimal(0);
            // 开始遍历每一页
            for (int i = 0; i < page; i++) {
                // 当前页面高度
                double pageHeight = 0;
                // 当前页面是否为最后一页  用于后续判断是否显示备注
                boolean isLastPage = i == page - 1;
                // 如果 所需明细行数 大于47行 那么就需要换页
                if (maxRow >= 42) {
                    // 获取47行明细的高度
                    pageHeight = calculatePageHeight(47);
                    if (maxRow >= 47) {
                        tempRow = 47;
                    } else {
                        tempRow = maxRow;
                    }
                    maxRow -= 47;
                } else {
                    // 根据明细获取
                    pageHeight = calculatePageHeight(maxRow);
                    if (pageHeight == 140) {
                        tempRow = 8;
                    } else {
                        tempRow = maxRow;
                    }
                }
                // 开始创建页面
                PageLayout pageLayout = ofdDoc.getPageLayout();
                // 设置页面宽度
                pageLayout.setWidth(OfdConstant.INVOICE_MIN_WIDTH);
                // 设置页面高度
                pageLayout.setHeight(pageHeight);
                // 创建虚拟页面
                VirtualPage virtualPage = new VirtualPage(pageLayout);
                // 画图
                org.ofdrw.layout.element.canvas.Canvas canvas = new Canvas(pageLayout);
                if (isLastPage) {
                    canvas.setDrawer((DrawContext dc) -> {
                        dc.moveTo(4.5315, 2.493);
                        dc.bezierCurveTo(4.5315, 1.368, 3.618, 0.45, 2.4885, 0.45);
                        dc.bezierCurveTo(1.3635, 0.45, 0.45, 1.368, 0.45, 2.493);
                        dc.bezierCurveTo(0.45, 3.618, 1.3635, 4.536, 2.4885, 4.536);
                        dc.bezierCurveTo(3.618, 4.536, 4.5315, 3.618, 4.5315, 2.493);
                        dc.moveTo(1.035, 1.035);
                        dc.lineTo(3.915, 3.915);
                        dc.moveTo(1.035, 3.915);
                        dc.lineTo(3.915, 1.035);
                        dc.stroke();
                    });
                    canvas.setXY(0D, 0D);
                    canvas.setPosition(Position.Absolute);
                    canvas.setLayer(Type.Background);
                    virtualPage.add(canvas);
                }


                // 构建头部标题
                buildHeadTitle(virtualPage, originalInvoice, page, i + 1, outPath);

                // 构建 购买人 销售人
                buildBuyerSeller(virtualPage, originalInvoice);

                // 构建明细信息
                index = buildDetail(virtualPage, originalInvoice, tempRow, subTotalAmount, subTotalTaxAmount, index, isLastPage, pageHeight, page);

                // 构建 底部 合计 小计 备注
                buildBottom(virtualPage, originalInvoice, subTotalAmount, subTotalTaxAmount, isLastPage, pageHeight, canvas);

                // 构建两侧边框 发票识别线
                buildBoderLine(virtualPage, pageHeight);

                // 像ofd文档内添加页面
                ofdDoc.addVPage(virtualPage);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw e;
        } finally {
            // 删除本地图片
            Paths.get(outPath.substring(0, outPath.lastIndexOf(File.separator)) + File.separator + "eCode.png").toFile().delete();
        }


        return outPath;
    }

    private int buildDetail(VirtualPage virtualPage, OriginalInvoice originalInvoice, int tempRow, BigDecimal subTotalAmount, BigDecimal subTotalTaxAmount, int index, boolean isLastPage, double pageHeight, int page) {
        // 项目名称
        Span titleSpan1 = new Span("        项目名称")
                .setFont(kaiFont)
                .setFontSize(3D)
                .setColor(139, 0, 15);
        Paragraph titleSpanP = new Paragraph(30D, 44D);
        titleSpanP.setXY(4.5D, 52.1D);
        titleSpanP.setPosition(Position.Absolute);
        titleSpanP.setLayer(Type.Background);
        titleSpanP.add(titleSpan1);
        virtualPage.add(titleSpanP);

        // 产权证书/不动产权证号
        Span titleSpan2 = new Span("  产权证书/不动产权证号")
                .setFont(kaiFont)
                .setFontSize(3D)
                .setColor(139, 0, 15);
        Paragraph titleSpanP2 = new Paragraph(39D, 44D);
        titleSpanP2.setXY(34.5D, 52.1D);
        titleSpanP2.setPosition(Position.Absolute);
        titleSpanP2.setLayer(Type.Background);
        titleSpanP2.add(titleSpan2);
        virtualPage.add(titleSpanP2);

        // 面积单位
        Span titleSpan3 = new Span(" 面积单位")
                .setFont(kaiFont)
                .setFontSize(3D)
                .setColor(139, 0, 15);
        Paragraph titleSpanP3 = new Paragraph(16D, 44D);
        titleSpanP3.setXY(73.5D, 52.1D);
        titleSpanP3.setPosition(Position.Absolute);
        titleSpanP3.setLayer(Type.Background);
        titleSpanP3.add(titleSpan3);
        virtualPage.add(titleSpanP3);

        // 数量
        Span titleSpan4 = new Span("         数 量")
                .setFont(kaiFont)
                .setFontSize(3D)
                .setColor(139, 0, 15);
        Paragraph titleSpanP4 = new Paragraph(21.5D, 44D);
        titleSpanP4.setXY(89.5D, 52.1D);
        titleSpanP4.setPosition(Position.Absolute);
        titleSpanP4.setLayer(Type.Background);
        titleSpanP4.add(titleSpan4);
        virtualPage.add(titleSpanP4);

        // 单价
        Span titleSpan5 = new Span("          单 价")
                .setFont(kaiFont)
                .setFontSize(3D)
                .setColor(139, 0, 15);
        Paragraph titleSpanP5 = new Paragraph(22.5D, 44D);
        titleSpanP5.setXY(111D, 52.1D);
        titleSpanP5.setPosition(Position.Absolute);
        titleSpanP5.setLayer(Type.Background);
        titleSpanP5.add(titleSpan5);
        virtualPage.add(titleSpanP5);


        // 金额
        Span titleSpan6 = new Span("          金 额")
                .setFont(kaiFont)
                .setFontSize(3D)
                .setColor(139, 0, 15);
        Paragraph titleSpanP6 = new Paragraph(23.5D, 44D);
        titleSpanP6.setXY(133.5D, 52.1D);
        titleSpanP6.setPosition(Position.Absolute);
        titleSpanP6.setLayer(Type.Background);
        titleSpanP6.add(titleSpan6);
        virtualPage.add(titleSpanP6);


        // 税率/征收率
        Span titleSpan7 = new Span("  税率/征收率")
                .setFont(kaiFont)
                .setFontSize(3D)
                .setColor(139, 0, 15);
        // 明细框
        Paragraph titleSpanP7 = new Paragraph(23D, 44D);
        titleSpanP7.setXY(157.5D, 52.1D);
        titleSpanP7.setPosition(Position.Absolute);
        titleSpanP7.setLayer(Type.Background);
        titleSpanP7.add(titleSpan7);
        virtualPage.add(titleSpanP7);


        //税额
        Span titleSpan8 = new Span("             税 额")
                .setFont(kaiFont)
                .setFontSize(3D)
                .setColor(139, 0, 15);
        // 明细框
        Paragraph titleSpanP8 = new Paragraph(27.5D, 44D);
        titleSpanP8.setXY(177D, 52.1D);
        titleSpanP8.setPosition(Position.Absolute);
        titleSpanP8.setLayer(Type.Background);
        titleSpanP8.add(titleSpan8);
        virtualPage.add(titleSpanP8);

        // 需要遍历明细信息列表 渲染到页面上
        List<RealEstateSalesInfo> realEstateSalesInfo = originalInvoice.getRealEstateSalesInfo();
        // 每一行需要追加的高度
        double tempHeight = 56.5D;
        for (int i = 0; index < realEstateSalesInfo.size(); i++) {
            // 取出一条明细信息
            RealEstateSalesInfo entity = realEstateSalesInfo.get(index);
            // 计算出每一行明细的所需高度
            // 项目名称高度
            int itemHeight = calculateRow(22, entity.getItem());
            // 不动产权高度
            int specHeight = calculateRow(23, entity.getPropertyCertificate());
            // 金额高度
            int amountHeight = calculateRow(15, entity.getAmount().toString());
            // 单价高度
            int priceHeight = calculateRow(15, entity.getPrice().toString());
            // 数量高度
            int quantityHeight = calculateRow(15, entity.getQuantity().toString());

            // 找出最大值
            int[] arr = {itemHeight, specHeight, amountHeight, priceHeight, quantityHeight};
            Arrays.sort(arr);

            // 当前行所需高度
            double rowHeight = arr[arr.length - 1] * 4.375D;
            // 定义行间隔变量
            double lineSpace = 0.5D;


            if (index == realEstateSalesInfo.size() - 1) {
                if (tempRow - arr[arr.length - 1] <= 0) {
                    break;
                }
            }

            // 如果空间足够
            if (tempRow >= arr[arr.length - 1]) {
                // 减少一条临时行数
                tempRow -= arr[arr.length - 1];
                // 项目名称
                if (StringUtil.isNotEmpty(entity.getItem())) {
                    Span itemSpan = new Span(entity.getItem())
                            .setFontSize(3D)
                            .setColor(0, 0, 0);
                    Paragraph itemP = new Paragraph(30D, rowHeight);
                    itemP.setXY(4.5D, tempHeight);
                    itemP.setPosition(Position.Absolute);
                    itemP.setLayer(Type.Body);
                    itemP.setLineSpace(lineSpace);
                    itemP.add(itemSpan);
                    virtualPage.add(itemP);
                }

                // 产权证书
                if (StringUtil.isNotEmpty(entity.getPropertyCertificate())) {
                    Span specSpan = new Span(entity.getPropertyCertificate())
                            .setFontSize(3D)
                            .setColor(0, 0, 0);
                    Paragraph specP = new Paragraph(37D, rowHeight);
                    specP.setXY(37D, tempHeight);
                    specP.setPosition(Position.Absolute);
                    specP.setLayer(Type.Body);
                    specP.setLineSpace(lineSpace);
                    specP.add(specSpan);
                    virtualPage.add(specP);
                }

                // 单位
                if (StringUtil.isNotEmpty(entity.getMeasurementDimension())) {
                    Span unitSpan = new Span(entity.getMeasurementDimension())
                            .setFontSize(3D)
                            .setColor(0, 0, 0);
                    Paragraph unitP = new Paragraph(16D, rowHeight);
                    unitP.setXY(calculateCenter(16D, 72.5D, entity.getMeasurementDimension()), tempHeight);
                    unitP.setPosition(Position.Absolute);
                    unitP.setLayer(Type.Body);
                    unitP.setLineSpace(lineSpace);
                    unitP.add(unitSpan);
                    virtualPage.add(unitP);
                }


                // 数量
                if (ObjectUtils.isNotEmpty(entity.getQuantity()) && entity.getQuantity().compareTo(BigDecimal.ZERO) != 0) {
                    Span quantitySpan = new Span(entity.getQuantity().toString())
                            .setFontSize(3D)
                            .setColor(0, 0, 0);
                    Paragraph quantityP = new Paragraph(22.5D, rowHeight);
                    if (entity.getQuantity().toString().length() >= 15) {
                        quantityP.setXY(calculateRight(22.5D, 87.5D, 15), tempHeight);
                    } else {
                        quantityP.setXY(calculateRight(22.5D, 87.5D, entity.getQuantity().toString()), tempHeight);
                    }
                    quantityP.setPosition(Position.Absolute);
                    quantityP.setLayer(Type.Body);
                    quantityP.setLineSpace(lineSpace);
                    quantityP.add(quantitySpan);
                    virtualPage.add(quantityP);
                }


                // 单价
                if (ObjectUtils.isNotEmpty(entity.getPrice()) && entity.getPrice().compareTo(BigDecimal.ZERO) != 0) {
                    Span priceSpan = new Span(entity.getPrice().toString())
                            .setFontSize(3D)
                            .setColor(0, 0, 0);
                    Paragraph priceP = new Paragraph(22.5D, rowHeight);
                    if (entity.getPrice().toString().length() >= 15) {
                        priceP.setXY(calculateRight(22.5D, 113D, 15), tempHeight);
                    } else {
                        priceP.setXY(calculateRight(22.5D, 113D, entity.getPrice().toString()), tempHeight);
                    }
                    priceP.setPosition(Position.Absolute);
                    priceP.setLayer(Type.Body);
                    priceP.setLineSpace(lineSpace);
                    priceP.add(priceSpan);
                    virtualPage.add(priceP);
                }

                // 金额
                if (ObjectUtils.isNotEmpty(entity.getAmount())) {
                    Span amountSpan = new Span(entity.getAmount().toString())
                            .setFontSize(3D)
                            .setColor(0, 0, 0);
                    Paragraph amountP = new Paragraph(23.5D, rowHeight);
                    if (entity.getAmount().toString().length() >= 15) {
                        amountP.setXY(calculateRight(23.5D, 137.5D, 15), tempHeight);
                    } else {
                        amountP.setXY(calculateRight(23.5D, 132.5D, entity.getAmount().toString()), tempHeight);
                    }
                    amountP.setPosition(Position.Absolute);
                    amountP.setLayer(Type.Body);
                    amountP.setLineSpace(lineSpace);
                    amountP.add(amountSpan);
                    virtualPage.add(amountP);
                    // 金额增加
                    subTotalAmount = subTotalAmount.add(entity.getAmount());
                }

                // 税率/征收率
                if (ObjectUtils.isNotEmpty(entity.getTaxScheme())) {
                    if (!"***".equals(entity.getTaxScheme())
                            && !YhzcbsEnum._03.getValue().equals(entity.getTaxScheme())
                            && !YhzcbsEnum._04.getValue().equals(entity.getTaxScheme())) {
                        entity.setTaxScheme(calculatePercent(entity.getTaxScheme()));
                    }
                    Span taxSchemeSpan = new Span(entity.getTaxScheme())
                            .setFontSize(3D)
                            .setColor(0, 0, 0);
                    Paragraph taxSchemeP = new Paragraph(23D, rowHeight);
                    taxSchemeP.setXY(calculateCenter(23D, 156.5D, entity.getTaxScheme()), tempHeight);
                    taxSchemeP.setPosition(Position.Absolute);
                    taxSchemeP.setLayer(Type.Body);
                    taxSchemeP.setLineSpace(lineSpace);
                    taxSchemeP.add(taxSchemeSpan);
                    virtualPage.add(taxSchemeP);
                }


                if (ObjectUtils.isNotEmpty(entity.getTaxAmount())) {
                    // 税额
                    Span taxSpan = new Span(entity.getTaxAmount())
                            .setFontSize(3D)
                            .setColor(0, 0, 0);
                    Paragraph taxP = new Paragraph(27.5D, rowHeight);
                    taxP.setXY(calculateRight(27.5D, 177.5D, entity.getTaxAmount()), tempHeight);
                    taxP.setPosition(Position.Absolute);
                    taxP.setLayer(Type.Body);
                    taxP.setLineSpace(lineSpace);
                    taxP.add(taxSpan);
                    virtualPage.add(taxP);
                    // 税额增加
                    if (!"***".equals(entity.getTaxAmount())) {
                        subTotalTaxAmount = subTotalTaxAmount.add(new BigDecimal(entity.getTaxAmount()));
                    }
                }
                // 渲染完毕 追加高度 用于下一行渲染
                tempHeight += rowHeight;
                index++;
            } else {
                break;
            }

        }
        // 小计 合计
        // 备注高度
        double remarkHeight = 0;
        if (isLastPage) {
            remarkHeight = 28;
        }
        if (page > 1) {
            // 小计
            Span subtotalSpan = new Span("            小        计")
                    .setFont(kaiFont)
                    .setFontSize(3D)
                    .setColor(139, 0, 15);
            Paragraph subtotalP = new Paragraph(201D, 6D);
            subtotalP.setXY(4.5D, pageHeight - (24.5D) - remarkHeight);
            subtotalP.setPosition(Position.Absolute);
            subtotalP.setLayer(Type.Background);
            subtotalP.add(subtotalSpan);
            virtualPage.add(subtotalP);
            // 小计内容
            Span subtotalAmountSpan = new Span("¥" + subTotalAmount.toString())
                    .setFontSize(3D)
                    .setColor(0, 0, 0);
            Paragraph subtotalAmountP = new Paragraph(30D, 6D);
            subtotalAmountP.setXY(calculateRight(26D, 125.5D, "¥" + subTotalAmount), pageHeight - (24.5D) - remarkHeight);
            subtotalAmountP.setPosition(Position.Absolute);
            subtotalAmountP.setLayer(Type.Background);
            subtotalAmountP.add(subtotalAmountSpan);
            virtualPage.add(subtotalAmountP);
            // 小计税额
            Span subtotalTaxSpan = new Span("¥" + subTotalTaxAmount.toString())
                    .setFontSize(3D)
                    .setColor(0, 0, 0);
            Paragraph subtotalTaxP = new Paragraph(30D, 6D);
            subtotalTaxP.setXY(calculateRight(27D, 178.5D, "¥" + subTotalTaxAmount), pageHeight - (24.5D) - remarkHeight);
            subtotalTaxP.setPosition(Position.Absolute);
            subtotalTaxP.setLayer(Type.Background);
            subtotalTaxP.add(subtotalTaxSpan);
            virtualPage.add(subtotalTaxP);
        }
        // 合计
        Span totalSpan = new Span("            合        计")
                .setFont(kaiFont)
                .setFontSize(3D)
                .setColor(139, 0, 15);
        Paragraph totalP = new Paragraph(201D, 6D);
        totalP.setXY(4.5D, pageHeight - (21D) - remarkHeight);
        totalP.setPosition(Position.Absolute);
        totalP.setLayer(Type.Background);
        totalP.add(totalSpan);
        virtualPage.add(totalP);
        // 合计内容
        Span totalAmountSpan = new Span("¥" + originalInvoice.getTaxExclusiveTotalAmount())
                .setFontSize(3D)
                .setColor(0, 0, 0);
        Paragraph totalAmountP = new Paragraph(30D, 6D);
        totalAmountP.setXY(calculateRight(26D, 125.5D, "¥" + originalInvoice.getTaxExclusiveTotalAmount()), pageHeight - (21D) - remarkHeight);
        totalAmountP.setPosition(Position.Absolute);
        totalAmountP.setLayer(Type.Background);
        totalAmountP.add(totalAmountSpan);
        virtualPage.add(totalAmountP);
        // 税额合计
        Span totalTaxSpan = new Span("¥" + originalInvoice.getTaxTotalAmount())
                .setFontSize(3D)
                .setColor(0, 0, 0);
        Paragraph totalTaxP = new Paragraph(30D, 6D);
        totalTaxP.setXY(calculateRight(27D, 178.5D, "¥" + originalInvoice.getTaxTotalAmount()), pageHeight - (21D) - remarkHeight);
        totalTaxP.setPosition(Position.Absolute);
        totalTaxP.setLayer(Type.Background);
        totalTaxP.add(totalTaxSpan);
        virtualPage.add(totalTaxP);
        return index;
    }

    /**
     * 根据明细信息 计算出所需行数
     *
     * @param goodsInfos
     * @return
     */
    private static int calculateNumberRows(List<RealEstateSalesInfo> goodsInfos) {
        int n = 0;
        for (int i = 0; i < goodsInfos.size(); i++) {
            RealEstateSalesInfo realEstateSalesInfo = goodsInfos.get(i);
            // 项目名称
            int item = calculateRow(22, realEstateSalesInfo.getItem());
            // 产权证书
            int property = calculateRow(23, realEstateSalesInfo.getPropertyCertificate());
            // 金额
            int amount = calculateRow(15, realEstateSalesInfo.getAmount().toString());
            // 单价
            int price = calculateRow(15, realEstateSalesInfo.getPrice().toString());
            // 数量
            int quantity = calculateRow(15, realEstateSalesInfo.getQuantity().toString());

            // 判断最大的高度 累加在n上
            int[] arr = {item, property, amount, price, quantity};
            Arrays.sort(arr);
            n += arr[arr.length - 1];
        }
        return n;
    }
}
